MicroPython tutorial for ESP8266
Must Watch!
MustWatch
MicroPython tutorial for ESP8266
This tutorial is intended to get you started using MicroPython on the ESP8266
system-on-a-chip.
If it is your first time it is recommended to follow the
tutorial through in the order below.
Otherwise the sections are mostly self
contained, so feel free to skip to those that interest you.
The tutorial does not assume that you know Python, but it also does not attempt
to explain any of the details of the Python language.
Instead it provides you
with commands that are ready to run, and hopes that you will gain a bit of
Python knowledge along the way.
To learn more about Python itself please refer
to https://www.python.org.
1. Getting started with MicroPython on the ESP8266
Using MicroPython is a great way to get the most of your ESP8266 board.
And
vice versa, the ESP8266 chip is a great platform for using MicroPython.
This
tutorial will guide you through setting up MicroPython, getting a prompt, using
WebREPL, connecting to the network and communicating with the Internet, using
the hardware peripherals, and controlling some external components.
Let's get started!
1.1. Requirements
The first thing you need is a board with an ESP8266 chip.
The MicroPython
software supports the ESP8266 chip itself and any board should work.
The main
characteristic of a board is how much flash it has, how the GPIO pins are
connected to the outside world, and whether it includes a built-in USB-serial
convertor to make the UART available to your PC.
The minimum requirement for flash size is 1Mbyte. There is also a special
build for boards with 512KB, but it is highly limited comparing to the
normal build: there is no support for filesystem, and thus features which
depend on it won't work (WebREPL, upip, etc.). As such, 512KB build will
be more interesting for users who build from source and fine-tune parameters
for their particular application.
Names of pins will be given in this tutorial using the chip names (eg GPIO0)
and it should be straightforward to find which pin this corresponds to on your
particular board.
1.2. Powering the board
If your board has a USB connector on it then most likely it is powered through
this when connected to your PC.
Otherwise you will need to power it directly.
Please refer to the documentation for your board for further details.
1.3. Getting the firmware
The first thing you need to do is download the most recent MicroPython firmware
.bin file to load onto your ESP8266 device. You can download it from the
MicroPython downloads page.
From here, you have 3 main choices
- Stable firmware builds for 1024kb modules and above.
- Daily firmware builds for 1024kb modules and above.
- Daily firmware builds for 512kb modules.
If you are just starting with MicroPython, the best bet is to go for the Stable
firmware builds. If you are an advanced, experienced MicroPython ESP8266 user
who would like to follow development closely and help with testing new
features, there are daily builds (note: you actually may need some
development experience, e.g. being ready to follow git history to know
what new changes and features were introduced).
Support for 512kb modules is provided on a feature preview basis. For end
users, it's recommended to use modules with flash of 1024kb or more. As
such, only daily builds for 512kb modules are provided.
1.4. Deploying the firmware
Once you have the MicroPython firmware (compiled code), you need to load it onto
your ESP8266 device.
There are two main steps to do this: first you
need to put your device in boot-loader mode, and second you need to copy across
the firmware.
The exact procedure for these steps is highly dependent on the
particular board and you will need to refer to its documentation for details.
If you have a board that has a USB connector, a USB-serial convertor, and has
the DTR and RTS pins wired in a special way then deploying the firmware should
be easy as all steps can be done automatically.
Boards that have such features
include the Adafruit Feather HUZZAH and NodeMCU boards.
For best results it is recommended to first erase the entire flash of your
device before putting on new MicroPython firmware.
Currently we only support esptool.py to copy across the firmware.
You can find
this tool here: https://github.com/espressif/esptool/, or install it
using pip:
pip install esptool
Versions starting with 1.3 support both Python 2.7 and Python 3.4 (or newer).
An older version (at least 1.2.1 is needed) works fine but will require Python
2.7.
Any other flashing program should work, so feel free to try them out or refer
to the documentation for your board to see its recommendations.
Using esptool.py you can erase the flash with the command:
esptool.py --port /dev/ttyUSB0 erase_flash
And then deploy the new firmware using:
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 esp8266-20170108-v1.8.7.bin
You might need to change the “port” setting to something else relevant for your
PC.
You may also need to reduce the baudrate if you get errors when flashing
(eg down to 115200).
The filename of the firmware should also match the file
that you have.
For some boards with a particular FlashROM configuration (e.g. some variants of
a NodeMCU board) you may need to use the following command to deploy
the firmware (note the -fm dio
option):
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect -fm dio 0 esp8266-20170108-v1.8.7.bin
If the above commands run without error then MicroPython should be installed on
your board!
1.5. Serial prompt
Once you have the firmware on the device you can access the REPL (Python prompt)
over UART0 (GPIO1=TX, GPIO3=RX), which might be connected to a USB-serial
convertor, depending on your board.
The baudrate is 115200.
The next part of
the tutorial will discuss the prompt in more detail.
1.6. WiFi
After a fresh install and boot the device configures itself as a WiFi access
point (AP) that you can connect to.
The ESSID is of the form MicroPython-xxxxxx
where the x's are replaced with part of the MAC address of your device (so will
be the same everytime, and most likely different for all ESP8266 chips).
The
password for the WiFi is micropythoN (note the upper-case N).
Its IP address
will be 192.168.4.1 once you connect to its network.
WiFi configuration will
be discussed in more detail later in the tutorial.
1.7. Troubleshooting installation problems
If you experience problems during flashing or with running firmware immediately
after it, here are troubleshooting recommendations:
- Be aware of and try to exclude hardware problems. There are 2 common problems:
bad power source quality and worn-out/defective FlashROM. Speaking of power
source, not just raw amperage is important, but also low ripple and noise/EMI
in general. If you experience issues with self-made or wall-wart style power
supply, try USB power from a computer. Unearthed power supplies are also known
to cause problems as they source of increased EMI (electromagnetic interference)
- at the very least, and may lead to electrical devices breakdown. So, you are
advised to avoid using unearthed power connections when working with ESP8266
and other boards. In regard to FlashROM hardware problems, there are independent
(not related to MicroPython in any way) reports
(e.g.)
that on some ESP8266 modules, FlashROM can be programmed as little as 20 times
before programming errors occur. This is much less than 100,000 programming
cycles cited for FlashROM chips of a type used with ESP8266 by reputable
vendors, which points to either production rejects, or second-hand worn-out
flash chips to be used on some (apparently cheap) modules/boards. You may want
to use your best judgement about source, price, documentation, warranty,
post-sales support for the modules/boards you purchase.
- The flashing instructions above use flashing speed of 460800 baud, which is
good compromise between speed and stability. However, depending on your
module/board, USB-UART convertor, cables, host OS, etc., the above baud
rate may be too high and lead to errors. Try a more common 115200 baud
rate instead in such cases.
- If lower baud rate didn't help, you may want to try older version of
esptool.py, which had a different programming algorithm:
pip install esptool==1.0.1
This version doesn't support
--flash_size=detect
option, so you will
need to specify FlashROM size explicitly (in megabits). It also requires
Python 2.7, so you may need to use pip2
instead of pip
in the
command above.
- The
--flash_size
option in the commands above is mandatory. Omitting
it will lead to a corrupted firmware.
- To catch incorrect flash content (e.g. from a defective sector on a chip),
add
--verify
switch to the commands above.
- Additionally, you can check the firmware integrity from a MicroPython REPL
prompt (assuming you were able to flash it and
--verify
option doesn't
report errors):
import esp
esp.check_fw()
If the last output value is True, the firmware is OK. Otherwise, it's
corrupted and need to be reflashed correctly.
- If you experience any issues with another flashing application (not
esptool.py), try esptool.py, it is a generally accepted flashing
application in the ESP8266 community.
- If you still experience problems with even flashing the firmware, please
refer to esptool.py project page, https://github.com/espressif/esptool
for additional documentation and bug tracker where you can report problems.
- If you are able to flash firmware, but
--verify
option or
esp.check_fw()
return errors even after multiple retries, you
may have a defective FlashROM chip, as explained above.
2. Getting a MicroPython REPL prompt
REPL stands for Read Evaluate Print Loop, and is the name given to the
interactive MicroPython prompt that you can access on the ESP8266.
Using the
REPL is by far the easiest way to test out your code and run commands.
There are two ways to access the REPL: either via a wired connection through the
UART serial port, or via WiFi.
2.1. REPL over the serial port
The REPL is always available on the UART0 serial peripheral, which is connected
to the pins GPIO1 for TX and GPIO3 for RX.
The baudrate of the REPL is 115200.
If your board has a USB-serial convertor on it then you should be able to access
the REPL directly from your PC.
Otherwise you will need to have a way of
communicating with the UART.
To access the prompt over USB-serial you need to use a terminal emulator program.
On Windows TeraTerm is a good choice, on Mac you can use the built-in screen
program, and Linux has picocom and minicom.
Of course, there are many other
terminal programs that will work, so pick your favourite!
For example, on Linux you can try running:
picocom /dev/ttyUSB0 -b115200
Once you have made the connection over the serial port you can test if it is
working by hitting enter a few times.
You should see the Python REPL prompt,
indicated by >>>
.
2.2. WebREPL - a prompt over WiFi
WebREPL allows you to use the Python prompt over WiFi, connecting through a
browser. The latest versions of Firefox and Chrome are supported.
For your convenience, WebREPL client is hosted at
http://micropython.org/webrepl . Alternatively, you can install it
locally from the the GitHub repository
https://github.com/micropython/webrepl .
Before connecting to WebREPL, you should set a password and enable it via
a normal serial connection. Initial versions of MicroPython for ESP8266
came with WebREPL automatically enabled on the boot and with the
ability to set a password via WiFi on the first connection, but as WebREPL
was becoming more widely known and popular, the initial setup has switched
to a wired connection for improved security:
import webrepl_setup
Follow the on-screen instructions and prompts. To make any changes active,
you will need to reboot your device.
To use WebREPL connect your computer to the ESP8266's access point
(MicroPython-xxxxxx, see the previous section about this).
If you have
already reconfigured your ESP8266 to connect to a router then you can
skip this part.
Once you are on the same network as the ESP8266 you click the “Connect” button
(if you are connecting via a router then you may need to change the IP address,
by default the IP address is correct when connected to the ESP8266's access
point).
If the connection succeeds then you should see a password prompt.
Once you type the password configured at the setup step above, press Enter once
more and you should get a prompt looking like >>>
.
You can now start
typing Python commands!
2.3. Using the REPL
Once you have a prompt you can start experimenting! Anything you type at the
prompt will be executed after you press the Enter key.
MicroPython will run
the code that you enter and print the result (if there is one).
If there is an
error with the text that you enter then an error message is printed.
Try typing the following at the prompt:
>>> print('hello esp8266!')
hello esp8266!
Note that you shouldn't type the >>>
arrows, they are there to indicate that
you should type the text after it at the prompt.
And then the line following is
what the device should respond with.
In the end, once you have entered the text
print("hello esp8266!")
and pressed the Enter key, the output on your screen
should look exactly like it does above.
If you already know some python you can now try some basic commands here.
For
example:
>>> 1 + 2
3
>>> 1 / 2
0.5
>>> 12**34
4922235242952026704037113243122008064
If your board has an LED attached to GPIO2 (the ESP-12 modules do) then you can
turn it on and off using the following code:
>>> import machine
>>> pin = machine.Pin(2, machine.Pin.OUT)
>>> pin.on()
>>> pin.off()
Note that on
method of a Pin might turn the LED off and off
might
turn it on (or vice versa), depending on how the LED is wired on your board.
To resolve this, machine.Signal class is provided.
2.3.1. Line editing
You can edit the current line that you are entering using the left and right
arrow keys to move the cursor, as well as the delete and backspace keys.
Also,
pressing Home or ctrl-A moves the cursor to the start of the line, and pressing
End or ctrl-E moves to the end of the line.
2.3.2. Input history
The REPL remembers a certain number of previous lines of text that you entered
(up to 8 on the ESP8266).
To recall previous lines use the up and down arrow
keys.
2.3.3. Tab completion
Pressing the Tab key will do an auto-completion of the current word that you are
entering.
This can be very useful to find out functions and methods that a
module or object has.
Try it out by typing “ma” and then pressing Tab.
It
should complete to “machine” (assuming you imported machine in the above
example).
Then type ”.” and press Tab again to see a list of all the functions
that the machine module has.
2.3.4. Line continuation and auto-indent
Certain things that you type will need “continuing”, that is, will need more
lines of text to make a proper Python statement.
In this case the prompt will
change to ...
and the cursor will auto-indent the correct amount so you can
start typing the next line straight away.
Try this by defining the following
function:
>>> def toggle(p):
... p.value(not p.value())
...
...
...
>>>
In the above, you needed to press the Enter key three times in a row to finish
the compound statement (that's the three lines with just dots on them).
The
other way to finish a compound statement is to press backspace to get to the
start of the line, then press the Enter key.
(If you did something wrong and
want to escape the continuation mode then press ctrl-C; all lines will be
ignored.)
The function you just defined allows you to toggle a pin.
The pin object you
created earlier should still exist (recreate it if it doesn't) and you can
toggle the LED using:
>>> toggle(pin)
Let's now toggle the LED in a loop (if you don't have an LED then you can just
print some text instead of calling toggle, to see the effect):
>>> import time
>>> while True:
... toggle(pin)
... time.sleep_ms(500)
...
...
...
>>>
This will toggle the LED at 1Hz (half a second on, half a second off).
To stop
the toggling press ctrl-C, which will raise a KeyboardInterrupt exception and
break out of the loop.
The time module provides some useful functions for making delays and doing
timing.
Use tab completion to find out what they are and play around with them!
2.3.5. Paste mode
Pressing ctrl-E will enter a special paste mode.
This allows you to copy and
paste a chunk of text into the REPL.
If you press ctrl-E you will see the
paste-mode prompt:
paste mode; Ctrl-C to cancel, Ctrl-D to finish
===
You can then paste (or type) your text in.
Note that none of the special keys
or commands work in paste mode (eg Tab or backspace), they are just accepted
as-is.
Press ctrl-D to finish entering the text and execute it.
2.3.6. Other control commands
There are four other control commands:
- Ctrl-A on a blank line will enter raw REPL mode.
This is like a permanent
paste mode, except that characters are not echoed back.
- Ctrl-B on a blank like goes to normal REPL mode.
- Ctrl-C cancels any input, or interrupts the currently running code.
- Ctrl-D on a blank line will do a soft reset.
Note that ctrl-A and ctrl-D do not work with WebREPL.
3. The internal filesystem
If your devices has 1Mbyte or more of storage then it will be set up (upon first
boot) to contain a filesystem.
This filesystem uses the FAT format and is
stored in the flash after the MicroPython firmware.
3.1. Creating and reading files
MicroPython on the ESP8266 supports the standard way of accessing files in
Python, using the built-in open()
function.
To create a file try:
>>> f = open('data.txt', 'w')
>>> f.write('some data')
9
>>> f.close()
The “9” is the number of bytes that were written with the write()
method.
Then you can read back the contents of this new file using:
>>> f = open('data.txt')
>>> f.read()
'some data'
>>> f.close()
Note that the default mode when opening a file is to open it in read-only mode,
and as a text file.
Specify 'wb'
as the second argument to open()
to
open for writing in binary mode, and 'rb'
to open for reading in binary
mode.
3.2. Listing file and more
The os module can be used for further control over the filesystem.
First
import the module:
>>> import os
Then try listing the contents of the filesystem:
>>> os.listdir()
['boot.py', 'port_config.py', 'data.txt']
You can make directories:
>>> os.mkdir('dir')
And remove entries:
>>> os.remove('data.txt')
3.3. Start up scripts
There are two files that are treated specially by the ESP8266 when it starts up:
boot.py and main.py.
The boot.py script is executed first (if it exists) and
then once it completes the main.py script is executed.
You can create these
files yourself and populate them with the code that you want to run when the
device starts up.
3.4. Accessing the filesystem via WebREPL
You can access the filesystem over WebREPL using the web client in a browser
or via the command-line tool. Please refer to Quick Reference and Tutorial
sections for more information about WebREPL.
4. Network basics
The network module is used to configure the WiFi connection.
There are two WiFi
interfaces, one for the station (when the ESP8266 connects to a router) and one
for the access point (for other devices to connect to the ESP8266).
Create
instances of these objects using:
>>> import network
>>> sta_if = network.WLAN(network.STA_IF)
>>> ap_if = network.WLAN(network.AP_IF)
You can check if the interfaces are active by:
>>> sta_if.active()
False
>>> ap_if.active()
True
You can also check the network settings of the interface by:
>>> ap_if.ifconfig()
('192.168.4.1', '255.255.255.0', '192.168.4.1', '8.8.8.8')
The returned values are: IP address, netmask, gateway, DNS.
4.1. Configuration of the WiFi
Upon a fresh install the ESP8266 is configured in access point mode, so the
AP_IF interface is active and the STA_IF interface is inactive.
You can
configure the module to connect to your own network using the STA_IF interface.
First activate the station interface:
>>> sta_if.active(True)
Then connect to your WiFi network:
>>> sta_if.connect('<your ESSID>', '<your password>')
To check if the connection is established use:
>>> sta_if.isconnected()
Once established you can check the IP address:
>>> sta_if.ifconfig()
('192.168.0.2', '255.255.255.0', '192.168.0.1', '8.8.8.8')
You can then disable the access-point interface if you no longer need it:
>>> ap_if.active(False)
Here is a function you can run (or put in your boot.py file) to automatically
connect to your WiFi network:
def do_connect():
import network
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
print('connecting to network...')
sta_if.active(True)
sta_if.connect('<essid>', '<password>')
while not sta_if.isconnected():
pass
print('network config:', sta_if.ifconfig())
4.2. Sockets
Once the WiFi is set up the way to access the network is by using sockets.
A socket represents an endpoint on a network device, and when two sockets are
connected together communication can proceed.
Internet protocols are built on top of sockets, such as email (SMTP), the web
(HTTP), telnet, ssh, among many others.
Each of these protocols is assigned
a specific port, which is just an integer.
Given an IP address and a port
number you can connect to a remote device and start talking with it.
The next part of the tutorial discusses how to use sockets to do some common
and useful network tasks.
5. Network - TCP sockets
The building block of most of the internet is the TCP socket.
These sockets
provide a reliable stream of bytes between the connected network devices.
This part of the tutorial will show how to use TCP sockets in a few different
cases.
5.1. Star Wars Asciimation
The simplest thing to do is to download data from the internet.
In this case
we will use the Star Wars Asciimation service provided by the blinkenlights.nl
website.
It uses the telnet protocol on port 23 to stream data to anyone that
connects.
It's very simple to use because it doesn't require you to
authenticate (give a username or password), you can just start downloading data
straight away.
The first thing to do is make sure we have the socket module available:
>>> import socket
Then get the IP address of the server:
>>> addr_info = socket.getaddrinfo("towel.blinkenlights.nl", 23)
The getaddrinfo
function actually returns a list of addresses, and each
address has more information than we need.
We want to get just the first valid
address, and then just the IP address and port of the server.
To do this use:
>>> addr = addr_info[0][-1]
If you type addr_info
and addr
at the prompt you will see exactly what
information they hold.
Using the IP address we can make a socket and connect to the server:
>>> s = socket.socket()
>>> s.connect(addr)
Now that we are connected we can download and display the data:
>>> while True:
... data = s.recv(500)
... print(str(data, 'utf8'), end='')
...
When this loop executes it should start showing the animation (use ctrl-C to
interrupt it).
You should also be able to run this same code on your PC using normal Python if
you want to try it out there.
5.2. HTTP GET request
The next example shows how to download a webpage.
HTTP uses port 80 and you
first need to send a “GET” request before you can download anything.
As part
of the request you need to specify the page to retrieve.
Let's define a function that can download and print a URL:
def http_get(url):
_, _, host, path = url.split('/', 3)
addr = socket.getaddrinfo(host, 80)[0][-1]
s = socket.socket()
s.connect(addr)
s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8'))
while True:
data = s.recv(100)
if data:
print(str(data, 'utf8'), end='')
else:
break
s.close()
Make sure that you import the socket module before running this function.
Then
you can try:
>>> http_get('http://micropython.org/ks/test.html')
This should retrieve the webpage and print the HTML to the console.
5.3. Simple HTTP server
The following code creates an simple HTTP server which serves a single webpage
that contains a table with the state of all the GPIO pins:
import machine
pins = [machine.Pin(i, machine.Pin.IN) for i in (0, 2, 4, 5, 12, 13, 14, 15)]
html = """<!DOCTYPE html>
<html>
<head> <title>ESP8266 Pins</title> </head>
<body> <h1>ESP8266 Pins</h1>
<table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
</body>
</html>
"""
import socket
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', addr)
while True:
cl, addr = s.accept()
print('client connected from', addr)
cl_file = cl.makefile('rwb', 0)
while True:
line = cl_file.readline()
if not line or line == b'\r\n':
break
rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
response = html % '\n'.join(rows)
cl.send(response)
cl.close()
6. GPIO Pins
The way to connect your board to the external world, and control other
components, is through the GPIO pins.
Not all pins are available to use,
in most cases only pins 0, 2, 4, 5, 12, 13, 14, 15, and 16 can be used.
The pins are available in the machine module, so make sure you import that
first.
Then you can create a pin using:
>>> pin = machine.Pin(0)
Here, the “0” is the pin that you want to access.
Usually you want to
configure the pin to be input or output, and you do this when constructing
it.
To make an input pin use:
>>> pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
You can either use PULL_UP or None for the input pull-mode.
If it's
not specified then it defaults to None, which is no pull resistor.
You can read the value on the pin using:
>>> pin.value()
0
The pin on your board may return 0 or 1 here, depending on what it's connected
to.
To make an output pin use:
>>> pin = machine.Pin(0, machine.Pin.OUT)
Then set its value using:
>>> pin.value(0)
>>> pin.value(1)
Or:
>>> pin.off()
>>> pin.on()
6.1. External interrupts
All pins except number 16 can be configured to trigger a hard interrupt if their
input changes.
You can set code (a callback function) to be executed on the
trigger.
Let's first define a callback function, which must take a single argument,
being the pin that triggered the function.
We will make the function just print
the pin:
>>> def callback(p):
... print('pin change', p)
Next we will create two pins and configure them as inputs:
>>> from machine import Pin
>>> p0 = Pin(0, Pin.IN)
>>> p2 = Pin(2, Pin.IN)
An finally we need to tell the pins when to trigger, and the function to call
when they detect an event:
>>> p0.irq(trigger=Pin.IRQ_FALLING, handler=callback)
>>> p2.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=callback)
We set pin 0 to trigger only on a falling edge of the input (when it goes from
high to low), and set pin 2 to trigger on both a rising and falling edge.
After
entering this code you can apply high and low voltages to pins 0 and 2 to see
the interrupt being executed.
A hard interrupt will trigger as soon as the event occurs and will interrupt any
running code, including Python code.
As such your callback functions are
limited in what they can do (they cannot allocate memory, for example) and
should be as short and simple as possible.
7. Pulse Width Modulation
Pulse width modulation (PWM) is a way to get an artificial analog output on a
digital pin.
It achieves this by rapidly toggling the pin from low to high.
There are two parameters associated with this: the frequency of the toggling,
and the duty cycle.
The duty cycle is defined to be how long the pin is high
compared with the length of a single period (low plus high time).
Maximum
duty cycle is when the pin is high all of the time, and minimum is when it is
low all of the time.
On the ESP8266 the pins 0, 2, 4, 5, 12, 13, 14 and 15 all support PWM.
The
limitation is that they must all be at the same frequency, and the frequency
must be between 1Hz and 1kHz.
To use PWM on a pin you must first create the pin object, for example:
>>> import machine
>>> p12 = machine.Pin(12)
Then create the PWM object using:
>>> pwm12 = machine.PWM(p12)
You can set the frequency and duty cycle using:
>>> pwm12.freq(500)
>>> pwm12.duty(512)
Note that the duty cycle is between 0 (all off) and 1023 (all on), with 512
being a 50% duty.
If you print the PWM object then it will tell you its current
configuration:
>>> pwm12
PWM(12, freq=500, duty=512)
You can also call the freq()
and duty()
methods with no arguments to
get their current values.
The pin will continue to be in PWM mode until you deinitialise it using:
>>> pwm12.deinit()
7.1. Fading an LED
Let's use the PWM feature to fade an LED.
Assuming your board has an LED
connected to pin 2 (ESP-12 modules do) we can create an LED-PWM object using:
>>> led = machine.PWM(machine.Pin(2), freq=1000)
Notice that we can set the frequency in the PWM constructor.
For the next part we will use timing and some math, so import these modules:
>>> import time, math
Then create a function to pulse the LED:
>>> def pulse(l, t):
... for i in range(20):
... l.duty(int(math.sin(i / 10 * math.pi) * 500 + 500))
... time.sleep_ms(t)
You can try this function out using:
>>> pulse(led, 50)
For a nice effect you can pulse many times in a row:
>>> for i in range(10):
... pulse(led, 20)
Remember you can use ctrl-C to interrupt the code.
7.2. Control a hobby servo
Hobby servo motors can be controlled using PWM.
They require a frequency of
50Hz and then a duty between about 40 and 115, with 77 being the centre value.
If you connect a servo to the power and ground pins, and then the signal line
to pin 12 (other pins will work just as well), you can control the motor using:
>>> servo = machine.PWM(machine.Pin(12), freq=50)
>>> servo.duty(40)
>>> servo.duty(115)
>>> servo.duty(77)
8. Analog to Digital Conversion
The ESP8266 has a single pin (separate to the GPIO pins) which can be used to
read analog voltages and convert them to a digital value.
You can construct
such an ADC pin object using:
>>> import machine
>>> adc = machine.ADC(0)
Then read its value with:
>>> adc.read()
58
The values returned from the read()
function are between 0 (for 0.0 volts)
and 1024 (for 1.0 volts).
Please note that this input can only tolerate a
maximum of 1.0 volts and you must use a voltage divider circuit to measure
larger voltages.
9. Power control
The ESP8266 provides the ability to change the CPU frequency on the fly, and
enter a deep-sleep state.
Both can be used to manage power consumption.
9.1. Changing the CPU frequency
The machine module has a function to get and set the CPU frequency.
To get the
current frequency use:
>>> import machine
>>> machine.freq()
80000000
By default the CPU runs at 80MHz.
It can be change to 160MHz if you need more
processing power, at the expense of current consumption:
>>> machine.freq(160000000)
>>> machine.freq()
160000000
You can change to the higher frequency just while your code does the heavy
processing and then change back when it's finished.
9.2. Deep-sleep mode
The deep-sleep mode will shut down the ESP8266 and all its peripherals,
including the WiFi (but not including the real-time-clock, which is used to wake
the chip).
This drastically reduces current consumption and is a good way to
make devices that can run for a while on a battery.
To be able to use the deep-sleep feature you must connect GPIO16 to the reset
pin (RST on the Adafruit Feather HUZZAH board).
Then the following code can be
used to sleep and wake the device:
import machine
# configure RTC.ALARM0 to be able to wake the device
rtc = machine.RTC()
rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP)
# set RTC.ALARM0 to fire after 10 seconds (waking the device)
rtc.alarm(rtc.ALARM0, 10000)
# put the device to sleep
machine.deepsleep()
Note that when the chip wakes from a deep-sleep it is completely reset,
including all of the memory.
The boot scripts will run as usual and you can
put code in them to check the reset cause to perhaps do something different if
the device just woke from a deep-sleep.
For example, to print the reset cause
you can use:
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
print('woke from a deep sleep')
else:
print('power on or hard reset')
10. Controlling 1-wire devices
The 1-wire bus is a serial bus that uses just a single wire for communication
(in addition to wires for ground and power).
The DS18B20 temperature sensor
is a very popular 1-wire device, and here we show how to use the onewire module
to read from such a device.
For the following code to work you need to have at least one DS18S20 or DS18B20 temperature
sensor with its data line connected to GPIO12.
You must also power the sensors
and connect a 4.7k Ohm resistor between the data pin and the power pin.
import time
import machine
import onewire, ds18x20
# the device is on GPIO12
dat = machine.Pin(12)
# create the onewire object
ds = ds18x20.DS18X20(onewire.OneWire(dat))
# scan for devices on the bus
roms = ds.scan()
print('found devices:', roms)
# loop 10 times and print all temperatures
for i in range(10):
print('temperatures:', end=' ')
ds.convert_temp()
time.sleep_ms(750)
for rom in roms:
print(ds.read_temp(rom), end=' ')
print()
Note that you must execute the convert_temp()
function to initiate a
temperature reading, then wait at least 750ms before reading the value.
11. Controlling NeoPixels
NeoPixels, also known as WS2812 LEDs, are full-colour LEDs that are connected in
serial, are individually addressable, and can have their red, green and blue
components set between 0 and 255.
They require precise timing to control them
and there is a special neopixel module to do just this.
To create a NeoPixel object do the following:
>>> import machine, neopixel
>>> np = neopixel.NeoPixel(machine.Pin(4), 8)
This configures a NeoPixel strip on GPIO4 with 8 pixels.
You can adjust the
“4” (pin number) and the “8” (number of pixel) to suit your set up.
To set the colour of pixels use:
>>> np[0] = (255, 0, 0) # set to red, full brightness
>>> np[1] = (0, 128, 0) # set to green, half brightness
>>> np[2] = (0, 0, 64) # set to blue, quarter brightness
Then use the write()
method to output the colours to the LEDs:
>>> np.write()
The following demo function makes a fancy show on the LEDs:
import time
def demo(np):
n = np.n
# cycle
for i in range(4 * n):
for j in range(n):
np[j] = (0, 0, 0)
np[i % n] = (255, 255, 255)
np.write()
time.sleep_ms(25)
# bounce
for i in range(4 * n):
for j in range(n):
np[j] = (0, 0, 128)
if (i // n) % 2 == 0:
np[i % n] = (0, 0, 0)
else:
np[n - 1 - (i % n)] = (0, 0, 0)
np.write()
time.sleep_ms(60)
# fade in/out
for i in range(0, 4 * 256, 8):
for j in range(n):
if (i // 256) % 2 == 0:
val = i & 0xff
else:
val = 255 - (i & 0xff)
np[j] = (val, 0, 0)
np.write()
# clear
for i in range(n):
np[i] = (0, 0, 0)
np.write()
Execute it using:
>>> demo(np)
12. Temperature and Humidity
DHT (Digital Humidity & Temperature) sensors are low cost digital sensors with
capacitive humidity sensors and thermistors to measure the surrounding air.
They feature a chip that handles analog to digital conversion and provide a
1-wire interface. Newer sensors additionally provide an I2C interface.
The DHT11 (blue) and DHT22 (white) sensors provide the same 1-wire interface,
however, the DHT22 requires a separate object as it has more complex
calculation. DHT22 have 1 decimal place resolution for both humidity and
temperature readings. DHT11 have whole number for both.
A custom 1-wire protocol, which is different to Dallas 1-wire, is used to get
the measurements from the sensor. The payload consists of a humidity value,
a temperature value and a checksum.
To use the 1-wire interface, construct the objects referring to their data pin:
>>> import dht
>>> import machine
>>> d = dht.DHT11(machine.Pin(4))
>>> import dht
>>> import machine
>>> d = dht.DHT22(machine.Pin(4))
Then measure and read their values with:
>>> d.measure()
>>> d.temperature()
>>> d.humidity()
Values returned from temperature()
are in degrees Celsius and values
returned from humidity()
are a percentage of relative humidity.
The DHT11 can be called no more than once per second and the DHT22 once every
two seconds for most accurate results. Sensor accuracy will degrade over time.
Each sensor supports a different operating range. Refer to the product
datasheets for specifics.
In 1-wire mode, only three of the four pins are used and in I2C mode, all four
pins are used. Older sensors may still have 4 pins even though they do not
support I2C. The 3rd pin is simply not connected.
Pin configurations:
Sensor without I2C in 1-wire mode (eg. DHT11, DHT22, AM2301, AM2302):
1=VDD, 2=Data, 3=NC, 4=GND
Sensor with I2C in 1-wire mode (eg. DHT12, AM2320, AM2321, AM2322):
1=VDD, 2=Data, 3=GND, 4=GND
Sensor with I2C in I2C mode (eg. DHT12, AM2320, AM2321, AM2322):
1=VDD, 2=SDA, 3=GND, 4=SCL
You should use pull-up resistors for the Data, SDA and SCL pins.
To make newer I2C sensors work in backwards compatible 1-wire mode, you must
connect both pins 3 and 4 to GND. This disables the I2C interface.
DHT22 sensors are now sold under the name AM2302 and are otherwise identical.
13. Next steps
That brings us to the end of the tutorial! Hopefully by now you have a good
feel for the capabilities of MicroPython on the ESP8266 and understand how to
control both the WiFi and IO aspects of the chip.
There are many features that were not covered in this tutorial.
The best way
to learn about them is to read the full documentation of the modules, and to
experiment!
Good luck creating your Internet of Things devices!
proteus 中仿真 micropython
Proteus 是一个支持仿真单片机的EDA软件,以前经常用来仿真 51、avr、pic、msp40等单片机,后来也逐步支持Arduino、树莓派和STM32,而在最新版本(v8.17 sp4)的 proteus 中,开始支持 micropython 仿真了。
下面就展示一下怎样使用 proteus 仿真 micropython 程序、仿真的效果,以及仿真中的问题。
首先需要安装最新版本的 proteus 软件,然后新建一个项目,并选择项目的名称和位置:
在项目向导中,选择创建原理图。
如果只是为了仿真,可以不用创建 PCB 文件。
然后选择创建固件项目(Create Firmware Project),并在系列(Family)中选择 MicroPython:
接下来在控制器(Controller)中选择 PI PICO,也就是树莓派的RP2040。
目前 proteus 支持 ESP32-S3 和 RP2040 两种型号的微控制器,推荐选择 PI PICO,因为目前软件对 ESP32-S3的支持不太完善,部分硬件功能还不能正常仿真,如硬件 I2C 功能。
确认参数无误后,单击 FInish 按钮完成项目设置。
完成项目设置后,就会显示如下界面:包含了原理图和代码编辑。
原理图部分已经包含了 RP2040(和PI PICO开发板一样,GPIO25连接到内部的LED,可以直接使用),代码编辑区中显示了默认的代码模板。
现在就可以和仿真其它电路一样,先添加各种元件,然后连接信号,再编写 MicroPython 代码,添加各种 MicroPython 库,最后进行仿真,验证电路设计或程序功能。
我们先在器件库中查找 I2C LCD,找到 I2C-16x2,也就是 I2C 接口的 LCD1602。
添加元件到原理图,连接 LCD1602 的 VDD和VSS,再将 SDA/SCL 连接到 RP2040 的 GP0/GP1,这两个GPIO被用于 I2C0(注意RP2040的硬件I2C只能使用几个特定引脚,而不是像ESP32那样任意引脚都可以)。
这里省略了 I2C 的上拉电阻,它对仿真结果没有太大影响。
在代码编辑器先输入下面代码,用 i2c.scan() 搜索一下器件地址:
from machine import Pin, I2C
i2c = I2C(0, scl=Pin(1), sda=Pin(0))
print(i2c.scan())
按下左下角的运行按钮,开始进行仿真,如果没有错误,就可以看到下面的结果,显示搜索到地址为63的设备。
如果出现错误就检查连线和代码,查找问题。
然后停止仿真,先从 github 或 gitee 下载社区的 i2c lcd1602的驱动库(https://gitee.com/microbit/mpy-lib/blob/master/lcd/I2C_LCD1602/i2c_lcd1602.py),从菜单中选择 project -> Add File,并选择下载后的 i2c_lcd1602.py 文件。
这样就可以将 i2c_lcd1602.py 文件添加到项目中,并在程序中使用了。
在 main.py 中编写一段测试代码,它先创建一个 i2c 对象,再创建一个 LCD1602 实例,最后在屏幕上显示一个不断递增的数字:
from machine import Pin, I2C
from i2c_lcd1602 import I2C_LCD1602
from time import sleep_ms
led = Pin(25, Pin.OUT)i2c = I2C(0, scl=Pin(1), sda=Pin(0))
print(i2c.scan())
lcd = I2C_LCD1602(i2c, 63)
n = 0
while 1: lcd.puts(n, 0, 0) n += 1 sleep_ms(1000)
运行仿真,我们就能看到下面的结果,可以看到仿真的效果和实际硬件上运行是一致的。
我们再从元件库中查找并添加一个 BME280 (温湿度、气压传感器),并将它的各引脚连接起来,I2C接口和LCD是共用的。
为了方便,这里将BME280的SDO引脚连接到了VCC,也就是设备地址为 0x77。
从社区的驱动库中(https://gitee.com/microbit/mpy-lib/blob/master/sensor/bme280/bme280.py)下载BME280的驱动,并添加到项目中。
修改mian.py,添加相关代码:
from machine import Pin, I2C
from bme280 import BME280
from i2c_lcd1602 import I2C_LCD1602
from time import sleep_ms
led = Pin(25, Pin.OUT)i2c = I2C(0, scl=Pin(1), sda=Pin(0))
print(i2c.scan())
bm = BME280(i2c, 0x77)lcd = I2C_LCD1602(i2c, 63)
n = 0
while 1:
led(not led())
n+=1
lcd.puts(n, 0, 0) lcd.puts(f"{bm.getHumi():.1f}%", 7, 0) lcd.puts(f"{bm.getTemp():.1f}C", 0, 1) lcd.puts(f"{bm.getPress()/100:.1f}P", 7, 1)
sleep_ms(500)
仿真运行,就可以在LCD上看到传感器的数值。
仿真时,可以通过 BME280 下方的三个红色按钮,改变传感器的数值,在屏幕上可以看到采集的数据会同步变化。
从上面的过程中可以发现,使用 proteus 可以方便的使用 micropython 调试电路,简化开发过程。
用同样方法可以测试更多器件,下面是 DS3232(高精度时钟)、MCP4018(数字电位器)、SHT21(温湿度传感器)的仿真效果。
大家还可以尝试更多不同传感器和模块,测试在 proteus 中的仿真效果。
使用proteus可以简化开发流程,在没有实际硬件的情况下进行开发,快速验证软件算法、硬件功能,提高开发效率。
但是也需要注意到软件仿真并不能完全取代硬件,毕竟软件仿真擅长的是算法和功能模拟,但是无法完全模拟硬件的全部功能,也无法仿真电气性能。
下面是目前发现的几个问题。
目前已知限制:支持主控芯片型号太少,而且对 ESP32-S3 的支持不好。
不支持 REPL 功能。
部分器件不能在 micropython 中仿真,如 DS18B20、I2C OLED 12864等。
虽然存在着一些限制,但是proteus的仿真功能仍然是一个非常强大的工具,只要善加利用,配合其它软件和硬件,就能发挥出巨大作用。